import { isNil, pick } from "lodash-es";
import { type ComponentType, type LazyExoticComponent, Suspense, useMemo, useRef } from "react";
import cx from "clsx";

import { useInteraction } from "../../store";
import { type TWidgetAnimateCss, type TWidget, isInternalField } from "../../register";
import { useWidgetState } from "./useWidgetState";

type TProps = {
    element?: LazyExoticComponent<any> | ComponentType<any>;
    width?: number;
    height?: number;
    left?: number;
    top?: number;
    style?: any;
    control?: any;
    name: any;
    id: any;
    animateCss?: TWidgetAnimateCss;
    groupFilterId?: string;
};

// 内置的动画
// 动画状态可能会与操控冲突
const animateCssMapping = new Map<any, string>([
    ["none", ""],
    ["animate__fadeInLeft", "animate__fadeInLeft"],
    ["animate__fadeInRight", "animate__fadeInRight"],
    ["animate__zoomIn", "animate__zoomIn"],
]);

export const Widget = (props: TProps) => {
    const { element: RcElement } = props;

    const subscribeFields = useRef(
        new Map<any, any>(Object.entries(props.control?.subscribe ?? {}).map((entry: any) => [entry[1]?.value ?? entry[1], entry[0]]))
    );

    const [subscribeState, dispatch] = useInteraction((state) => {
        const current = { widgetSubscribe: {} as any, elementSubscribe: {} as any };

        if (subscribeFields.current.size === 0 || isNil(state)) {
            return current;
        }

        subscribeFields.current.forEach((value, key) => {
            if (key === state.type) {
                if (isInternalField(state.type)) {
                    current.widgetSubscribe[value] = state.payload;
                } else {
                    current.elementSubscribe[value] = state.payload;
                }
            }
        });

        return current;
    });

    const { widgetVisible, showWidgetGroup } = useWidgetState({
        widgetSubscribe: subscribeState.widgetSubscribe,
        groupFilterId: props.groupFilterId,
        dispatch,
    });

    const el = useMemo(() => {
        try {
            if (!RcElement) return null;
            return <RcElement {...(subscribeState.elementSubscribe ?? {})} dispatch={dispatch} showWidgetGroup={showWidgetGroup} />;
        } catch (error) {
            console.log("%c[WIDGET ERROR]", "color:red;", error);
        }
    }, [RcElement, subscribeState.elementSubscribe, dispatch, showWidgetGroup]);

    if (!RcElement || !widgetVisible) return null;

    const widgetRootStyle: any = Object.assign(
        {
            left: 0,
            top: 0,
            width: 0,
            height: 0,
        },
        {
            ...pick(props, ["width", "height", "left", "top", "zIndex"]),
            ...pick(props.style, ["pointerEvents"]),
            position: "absolute",
            display: widgetVisible ? "block" : "none",
            opacity: widgetVisible ? 1 : 0,
        }
    );

    return (
        <section
            className={cx("widget", {
                [`animate__animated ${animateCssMapping.get(props.animateCss ?? "none")}`]: props.animateCss,
            })}
            data-widget-name={props.name}
            data-widget-id={props.id}
            data-group-filter-id={props.groupFilterId}
            style={widgetRootStyle}
        >
            <Suspense fallback={<span>loading</span>}>
                <div className="w-full h-full" style={{ position: "absolute", left: 0, top: 0 }}>
                    {el}
                </div>
            </Suspense>
        </section>
    );
};

export type { TWidget };
